/******************************************************************************
 * Copyright (C) 671643387@qq.com
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 *****************************************************************************/

#include "NodeCpp/Thread_POSIX.h"
#include <cassert>
#include <unistd.h>
#include <sched.h>
#include "NodeCpp/Macros.h"
#include "NodeCpp/Runnable.h"

namespace NodeCpp
{
    ThreadImpl::ThreadImpl(void)
        : ID_(0)
        , Runnable_(NULL)
    	, Priority_(PRIO_NORMAL_IMPL)
    {

    }

    ThreadImpl::~ThreadImpl(void)
    {

    }

    bool ThreadImpl::StartImpl(Runnable* _Runnable)
    {
        NODECPP_ASSERT(ID_ == 0L);
        Runnable_ = _Runnable;
        if (pthread_create(&ID_, NULL, ThreadImpl::_Proc, this) == 0) {
        	return true;
        }
        ID_ = 0L;
        return false;
    }

    void ThreadImpl::JoinImpl(void)
    {
        NODECPP_ASSERT(ID_ != 0L);
        if (ID_ != 0L) {
            pthread_join(ID_, NULL);
        }
    }

    bool ThreadImpl::JoinableImpl(void)
    {
        return ID_ != 0L;
    }

    bool ThreadImpl::SetPriorityImpl(int _Priority)
    {
#if defined(_POSIX_THREAD_PRIORITY_SCHEDULING)
    	int _Policy;
    	pthread_attr_t _ThreadAttr;
    	struct sched_param _ThreadParam;
    	if (pthread_attr_getschedpolicy(&_ThreadAttr, &_Policy) != 0) {
    		return false;
    	}
    	if (pthread_attr_getschedparam(&_ThreadAttr, &_ThreadParam) != 0) {
    		return false;
    	}

    	int _PriorityMin = sched_get_priority_min(_Policy);
    	int _PriorityMax = sched_get_priority_max(_Policy);
    	int _PriorityStep = (_PriorityMax - _PriorityMin) / 5;

    	_ThreadParam.sched_priority = _PriorityMin + _PriorityStep * _Priority;
    	if (pthread_attr_setschedparam(&_ThreadAttr, &_ThreadParam) != 0) {
    		return false;
    	}

    	Priority_ = Priority(_Priority);
#endif
        return true;
    }

    void* ThreadImpl::_Proc(void* _Arg)
        {
            NODECPP_ASSERT(_Arg != NULL);
            ThreadImpl* _TH = static_cast<ThreadImpl*>(_Arg);
            _TH->Runnable_->Run();
            return 0;
        }

    ThreadImpl::ID ThisThreadImpl::GetIDImpl(void)
    {
        return pthread_self();
    }

    void ThisThreadImpl::SleepImpl(long _Milliseconds)
    {
        usleep(int(_Milliseconds * 1000));
    }
}
